import BallItem, { EnumStatus } from './item/BallItem'
import BrickItem from './item/BrickItem'

import Model from './Model'
import EventDispatch, { Event_Name } from '../common/event/EventDispatch'

import { AudioPlayer } from '../common/audio/AudioPlayer'
import { AUDIO_CONFIG } from '../service/Constant'
import LoaderMgr from '../common/loader/LoaderMgr'
import { gen_handler } from '../common/utils'
import { Tween, Ease } from '../common/tween/Tween'
import { RandomUtil } from '../common/random/RandomUtil'
import { TimerMgr } from '../common/timer/TimerMgr'
import * as ui from "../common/ui/popMgr"
import {UI_CONFIG} from '../service/Constant'

const { ccclass, property } = cc._decorator;

@ccclass
export default class GameControl extends cc.Component {

    @property(cc.Node)
    bg: cc.Node = null;
    @property(cc.Node)
    node_top: cc.Node = null;
    @property(cc.Node)
    cannon_head: cc.Node = null;
    @property(cc.Node)
    node_physics: cc.Node = null;
    @property(cc.Label)
    lb_ball_count: cc.Label = null;
    @property(cc.Label)
    lb_ball_power: cc.Label = null;
    @property(cc.Node)
    node_freeze: cc.Node = null;

    @property(cc.Label)
    lb_score: cc.Label = null;
    @property(cc.Node)
    node_power_progress: cc.Node = null;
    @property([cc.Node])
    power_txts: cc.Node[] = [];

    @property(cc.ParticleSystem)
    particleSystem: cc.ParticleSystem = null;

    @property([cc.Prefab])
    balls_ins: cc.Prefab[] = []; // 发射求
    @property([cc.Prefab])
    bricks_ins: cc.Prefab[] = [];

    @property(cc.Node)
    node_star_img: cc.Node = null;
    @property(cc.Prefab)
    star_ins: cc.Prefab = null;

    private _star_pool: cc.Node[] = [];
    private _star_num = 0;

    private balls_pool: cc.Node[][] = [];
    private bricks_pool: cc.Node[][] = []; // 砖块

    private balls_in_game: BallItem[] = [];
    private bricks_in_game: BrickItem[] = [];

    private _updateDt: number = 0;
    private _brick_speed: number = 1;
    private _moved_length = 0;
    private _moved_level = 0;
    private _power_type = 0;
    private _isGameOver = false;
    private _isPaused = false

    onLoad() {
        this.bg.on(cc.Node.EventType.TOUCH_MOVE, this.onTouchMove, this);
        cc.director.getPhysicsManager().enabled = true;
        cc.director.getPhysicsManager().enabledAccumulator = true;

        for (let i = 0, len = this.balls_ins.length; i < len;) {
            this.balls_pool[i++] = [];
        }

        for (let i = 0, len = this.bricks_ins.length; i < len;) {
            this.bricks_pool[i++] = [];
        }

        this.init()
    }

    /**
     * touchMove
     */
    onTouchMove(e: cc.Event.EventTouch) {
        const deltaX = e.getDeltaX();
        const deltaY = e.getDeltaY();
        const deltaR = Math.sqrt(deltaX * deltaX + deltaY * deltaY) * 0.3;
        const sign = Math.sign(deltaX);

        let angle = -this.cannon_head.angle + deltaR * sign;
        angle = Math.abs(angle) >= 85 ? Math.sign(angle) * 85 : angle;
        this.cannon_head.angle = -angle;
    }

    init() {
        this.addEvent()
        this.startGame()
        // AudioPlayer.ins().play_music(AUDIO_CONFIG.Audio_Bgm);
    }

    addEvent() {
        EventDispatch.ins().add(Event_Name.GAME_CREATE_BALL, this.createBall, this);
        EventDispatch.ins().add(Event_Name.GAME_RELIVE, this.gameRelive, this);
        EventDispatch.ins().add(Event_Name.GAME_ON_TOUCH_MOVE, this.onTouchMove, this);
        EventDispatch.ins().add(Event_Name.GAME_POWER_TYPE_CHANGED, this.updateGamePowerType, this);
        EventDispatch.ins().add(Event_Name.GAME_SCORE_CHANGED, this.updateScore, this, true);
        EventDispatch.ins().add(Event_Name.GAME_BALL_POWER_CHANGED, this.updateBallPower, this, true);
        EventDispatch.ins().add(Event_Name.GAME_PLAY_BRICK_REMOVE_EFFECT, this.playBrickDeleteEffect, this);
        EventDispatch.ins().add(Event_Name.GAME_STAR_GET_EFFECT, this.updateStarNumGetEffect, this);
        EventDispatch.ins().add(Event_Name.GO_HOME, this.closeGame, this);
        EventDispatch.ins().add(Event_Name.CONTINUE, this.continueGame, this);
    }

    removeEvent() {
        EventDispatch.ins().remove(Event_Name.GAME_CREATE_BALL, this.createBall);
        EventDispatch.ins().remove(Event_Name.GAME_RELIVE, this.gameRelive);
        EventDispatch.ins().remove(Event_Name.GAME_ON_TOUCH_MOVE, this.onTouchMove);
        EventDispatch.ins().remove(Event_Name.GAME_POWER_TYPE_CHANGED, this.updateGamePowerType);
        EventDispatch.ins().remove(Event_Name.GAME_SCORE_CHANGED, this.updateScore);
        EventDispatch.ins().remove(Event_Name.GAME_PLAY_BRICK_REMOVE_EFFECT, this.playBrickDeleteEffect);
        EventDispatch.ins().remove(Event_Name.GAME_STAR_GET_EFFECT, this.updateStarNumGetEffect);
        EventDispatch.ins().remove(Event_Name.GO_HOME, this.closeGame);
        EventDispatch.ins().remove(Event_Name.CONTINUE, this.continueGame);
    }

    startGame() {
        this.node_physics.active = true
        this.cannon_head.angle = 10 - Math.random() * 20
        cc.director.getPhysicsManager().enabled = true

        this._updateDt = 0
        this._moved_length = 0
        this._moved_level = 0
        this._isGameOver = false

        Model.ins().reset()
        // 初始化球
        const ballCount = Model.ins().ballInitCount
        for (let i = 0; i < ballCount; i++) {
            this.createBall()
        }

        // 初始化障碍物
        const brick_radius = Model.ins().brickRadius;
        for (let i = 0; i < 2; i++) {
            for (let j = -3; j < 4; j++) {
                let hp = (i + 1) * Model.ins().ball_power + (ballCount - 4)
                hp = hp < 0 ? 1 : hp

                this.createBrick(j * brick_radius * 2 + Model.ins().brickInitX, i * brick_radius * 2 + Model.ins().brickInitY, hp);
            }
        }

        this._star_num = Model.ins().ball_power;
        this.setStartNum()
    }


    update(dt) {
        TimerMgr.getInst().update(dt);
        if (this._isGameOver) {
            return;
        }
        if(this._isPaused) {
            return
        }

        if (this._power_type > 0) {
            this.node_power_progress.width -= 1;
            if (this.node_power_progress.active && this.node_power_progress.width <= 0) {
                this.updateGamePowerType(0);
            }

            switch (this._power_type) {
                case 3: {
                    this._brick_speed = 0;
                    break;
                }
            }
        }

        this._updateDt++;
        this._moved_length += this._brick_speed;

        // 发射球
        if (this._updateDt % Model.ins().fireBallDt === 0 || this._power_type === 2) {
            this.balls_in_game.some((item) => {
                if (item.status === EnumStatus.onReady && (item.type === 0 || this._power_type === 2)) {
                    item.powerScale = this._power_type === 1 ? 2 : 1;
                    item.fireBall(-this.cannon_head.angle);
                    return true;
                }
            })
        }

        // 添加敌人
        const brick_radius = Model.ins().brickRadius;
        const ball_power = Model.ins().ball_power;
        const new_level = Math.floor(this._moved_length / 4 / brick_radius);
        if (new_level > this._moved_level) {
            this._moved_level++;
            let balls_in_game_length = this.balls_in_game.length / (this._power_type === 2 ? 2 : 1);
            const maxHp = Math.ceil(balls_in_game_length * ball_power * 0.5 + this._moved_level * ball_power * 1.2);
            const brick_type_percent = Model.ins().brick_type_percent;
            let big_j = -999;
            let item_i = -999, item_j = -999;
            if (this._moved_level % 6 === 0) {
                //big
                big_j = RandomUtil.ins().randomNum(-2, 3);
                this.createBrick(-brick_radius + big_j * brick_radius * 2 + Model.ins().brickInitX, brick_radius + Model.ins().brickInitY,
                    RandomUtil.ins().randomNum(maxHp * 2, maxHp * 6),
                    RandomUtil.ins().randomNum(13, 16),
                    // 13,
                    10);
            }
            if (this._moved_level % 11 === 0) {
                //道具
                item_i = RandomUtil.ins().randomNum(0, 1);
                item_j = RandomUtil.ins().randomNum(-3, 3);
            }
            for (let i = 0; i < 2; i++) {
                for (let j = -3; j < 4; j++) {
                    if (j === big_j || j === big_j - 1) {
                        //big
                    } else {
                        let hp = (i === item_i && j === item_j) ? RandomUtil.ins().randomNum(ball_power, Math.ceil(maxHp / 2)) : RandomUtil.ins().randomNum(-Math.round(maxHp / (this._moved_level % 5 + 1)), maxHp);
                        let brick_type = (i === item_i && j === item_j) ? RandomUtil.ins().randomNum(11, 13) : (brick_type_percent[RandomUtil.ins().randomNum(0, brick_type_percent.length - 1)]);
                        if (hp >= ball_power * 2) {
                            this.createBrick(j * brick_radius * 2 + Model.ins().brickInitX, i * brick_radius * 2 + Model.ins().brickInitY,
                                hp,
                                brick_type,
                                Math.ceil(hp * 10 / maxHp));
                        }
                    }
                }
            }
        }

        // 敌人呢移动
        let brick_min_y = 9999;
        for (let index = 0, len = this.bricks_in_game.length; index < len; index++) {
            const element = this.bricks_in_game[index];
            if (element && element.node) {
                const brick = element.node;
                if (element.hp <= 0 || !brick.active) {
                    //remove
                    if (this._updateDt % 60 === 0) {
                        element.reset();
                        this.bricks_in_game.splice(index, 1);
                        this.bricks_pool[element.brick_type].push(brick);
                        index--;
                    }
                } else {
                    //update pos
                    brick.y -= this._brick_speed;
                    if (brick.y - element.brick_radius_mul * brick_radius <= Model.ins().ballInitY) {
                        this.gameOver();
                        return;
                    }
                    if (brick.y < brick_min_y) {
                        brick_min_y = brick.y;
                    }
                }
            }
        }

        this._brick_speed = (brick_min_y > Model.ins().ballInitY + brick_radius * 7) ? 0.6 : ((brick_min_y > Model.ins().ballInitY + brick_radius * 5) ? 0.4 : 0.2);



    }

    // 创建球、子弹
    createBall(x = Model.ins().ballInitX, y = Model.ins().ballInitY, status = 0, type = 0) {
        let ball = this.balls_pool[type].shift();

        if (!ball) {
            ball = cc.instantiate(this.balls_ins[type])
            this.node_physics.addChild(ball)
        }
        const item = ball.getComponent(BallItem)
        item.init(x, y, status)
        ball.active = true

        // 运行的球
        this.balls_in_game.unshift(item)
        this.setBallCount()

        if (type === 0) {
            this.createBall(Model.ins().ballInitX, y = Model.ins().ballInitY, 0, 1);
        }
    }

    // 敌人呢
    createBrick(x, y, hp = 1, type = 0, color_num = 1) {
        if (type === 7 && this.balls_in_game.length > 200) {
            type = 0;
        }
        let brick = this.bricks_pool[type].shift()
        if (!brick) {
            brick = cc.instantiate(this.bricks_ins[type])
            this.node_physics.addChild(brick)
        }

        const item = brick.getComponent(BrickItem);
        brick.active = true;
        brick.x = x;
        brick.y = y;
        item.init(color_num, hp);
        this.bricks_in_game.push(item);
    }

    // 设置小球数
    setBallCount() {
        if (this.lb_ball_count) {
            this.lb_ball_count.string = `${this.balls_in_game.length / 2}`;
        }
    }

    setStartNum() {
        if (this.lb_ball_power) {
            this.lb_ball_power.string = `${this._star_num}`;
        }
    }

    // 复活
    gameRelive() {
        Model.ins().revive_times++;
        this.bricks_in_game.forEach((brick) => {
            brick.reset();
            this.bricks_pool[brick.brick_type].push(brick.node);
        })
        this.bricks_in_game = [];
        this._isGameOver = false;
        cc.director.getPhysicsManager().enabled = true;
       // AudioPlayer.ins().play_music(AUDIO_CONFIG.Audio_Bgm);
    }

    private gameOver() {
        this._isGameOver = true;
        cc.director.getPhysicsManager().enabled = false;
        ui.PopMgr.ins().show(UI_CONFIG.result, null, this);
    }

    updateBtnPower(e, eData) {
        this.updateGamePowerType(Number(eData))
    }

    // 炮台威力
    updateGamePowerType(power = 0) {
        if (power > 0) {
            if (this._power_type === 0) {
                this.node_power_progress.active = true
                this.node_power_progress.width = 640
                this._power_type = power

                switch (power) {
                    case 2:
                        this.cannon_head.scale = 2
                        break
                    case 3:
                        this.node_freeze.active = true
                        break
                }

                this.lb_ball_count.node.color = this.node_power_progress.color = [cc.Color.WHITE, cc.Color.RED, cc.Color.YELLOW, cc.Color.GRAY][power] || cc.Color.WHITE;
                const power_txt = this.power_txts[power]

                if (power_txt) {
                    power_txt.active = true
                    power_txt.stopAllActions()
                    power_txt.opacity = 0

                    const moveY = 100
                    power_txt.runAction(cc.sequence(cc.fadeIn(1), cc.moveBy(0.5, 0, moveY), cc.delayTime(0.5), cc.fadeOut(1), cc.callFunc(() => {
                        if (power_txt) {
                            power_txt.y -= moveY;
                            power_txt.active = false;
                        }
                    })))
                }
                AudioPlayer.ins().play_sound(AUDIO_CONFIG.Audio_levelup)
            } 
        } else {
            this.node_power_progress.active = false
            this.node_freeze.active = false
            this.lb_ball_count.node.color = cc.Color.WHITE

            this.cannon_head.scale = 1

            this._power_type = power
        }
    }

    // 更新分数
    updateScore() {
        const score = Model.ins().score;
        this.lb_score.string = `${score}`;
    }

    // 添加小球威力
    updateBallPower(oval?: number, nval?: number) {
        const ball_power = Model.ins().ball_power;
       // this.lb_ball_power.string = `${ball_power}`;
    }


    // 粒子特效
    private _brick_img_pool: cc.Node[] = [];
    playBrickDeleteEffect(x: number, y: number, color: cc.Color, icon: cc.Node) {
        const theme_cfg = Model.ins().theme_config[0]
        if (theme_cfg) {
            LoaderMgr.getInstance().loadAsset('texture/plist/customize', gen_handler((res: cc.SpriteAtlas) => {
                const spriteFrame = res.getSpriteFrame(theme_cfg.theme);
                if (spriteFrame) {
                    for (let index = 0; index < 9; index++) {
                        const i = index % 3 - 1.5
                        const j = Math.floor(index / 3) - 1.5

                        const posX = x + i * (100 + 150 * Math.random())
                        const posY = y + j * (100 + 150 * Math.random())

                        let img = this._brick_img_pool.shift()
                        if (!img) {
                            img = new cc.Node()
                            img.addComponent(cc.Sprite)
                            this.node_physics.addChild(img)
                        }
                        img.active = true
                        img.angle = 0
                        img.getComponent(cc.Sprite).spriteFrame = icon.getComponent(cc.Sprite).spriteFrame// spriteFrame
                        img.color = color
                        img.opacity = 255
                        const size = Math.random() * 5 + 15
                        img.width = img.height = size;
                        img.x = x;
                        img.y = y;

                        Tween.get(img).to({ x: posX, y: posY, width: size / 3, height: size / 3, angle: 1000 * Math.random() }, Math.random() * 500 + 500).call(() => {
                            img.active = false;
                            this._brick_img_pool.push(img);
                        })
                    }

                }
            }), cc.SpriteAtlas)
        }
    }

    //星星特效
    private updateStarNumGetEffect(x: number, y: number, count: number) {
        const targetPos = this.node_top.convertToNodeSpaceAR(this.node_star_img.convertToWorldSpaceAR(cc.v2(0, 0)));
        for (let index = 0; index < count; index++) {
            let star_item = this._star_pool.shift();
            if (!star_item) {
                star_item = cc.instantiate(this.star_ins);
                this.node_top.addChild(star_item);
            }
            star_item.x = x;
            star_item.y = y;
            star_item.angle = 0;
            star_item.active = true;
            Tween.get(star_item).to({ angle: 720, x: targetPos.x, y: targetPos.y }, 800 + 100 * index, Ease.getBackInOut(1.2)).call(() => {
                star_item.active = false;
                this._star_pool.push(star_item);
                Tween.get(this.node_star_img).to({ scale: 1.2 }, 300).to({ scale: 1 }, 300, Ease.backInOut).call(() => {
                    this._star_num += 1;
                    this.lb_ball_power.string = `${this._star_num}`;
                })
            })
        }
    }

    onClose() {
        if(!this._isGameOver && !this._isPaused) {
            this._isPaused = true
            this.showPausePanel()
        } else {
            this.closeGame()
        }
    }

    // 暂停弹窗
    showPausePanel() {
        ui.PopMgr.ins().show(UI_CONFIG.pause)
    }

    closeGame() {
        this.removeEvent()
        this.clearGame()
        AudioPlayer.ins().play_sound(AUDIO_CONFIG.Audio_Btn);
        cc.director.loadScene('home')
    }

    continueGame() {
        this._isPaused = false
    }

    clearGame() {
        this.node_physics.active = false
        cc.director.getPhysicsManager().enabled = false
        this.balls_in_game.forEach((ball) => {
            ball.reset();
            this.balls_pool[ball.type].push(ball.node);
        })
        this.balls_in_game = [];

        this.bricks_in_game.forEach((brick) => {
            brick.reset();
            this.bricks_pool[brick.brick_type].push(brick.node);
        })

        this.bricks_in_game = [];
        Model.ins().reset();
    }

    addBall() {
        for(let i =0; i<10; i++) {
            this.createBall()
        }
    }

    addStar() {
        Model.ins().ball_power += 10
        this._star_num = Model.ins().ball_power
        this.setStartNum()
    }
}
